package JTree;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
import javax.swing.plaf.basic.BasicTreeUI;
import java.awt.BorderLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

/**
 * Java6 JTree ClipBoard Copy Cut Paste Test + File Drop Copy Paste
 * 
 * Java6 JTree クリップボードにコピー、切り取り、貼り付けテスト + ファイルドロップ、コピペ
 * 
 * 
 * 
 * サンプルなので１つのファイルに必要なクラスを全部詰め込んであります。
 * 
 * 
 * 
 * 起動すると、JTreeが２枚表示されます。
 * 
 * JTree間でコピペ、カトペ、ドラッグアンドドロップできます。
 * 
 * 他のアプリケーションからドロップしようとしても禁止マークがでます。
 * 
 * DnDでムーブ、Ctrl+DnDでコピーも有効です。
 * 
 * ※JTreeDnDTest.javaをベースに作成したのですが、コピペ対応のために弄ってる間に、対応できちゃったようです。
 * 
 * 
 * 
 * エクスプローラからのファイルのコピペ、DnDに対応します。
 * 
 * 文字列のコピペ、DnDも対応してみました。
 * 
 * 
 * 
 * 自分自身にドロップ禁止、子ノードにドロップ禁止してみた。
 * 
 * canImportの中で判定するとうまくいかなかったので、ドロップ後に判定するようにしてみた。(canImport2)
 * 
 * exportAsDragで、JTreeをクラス変数に設定して、exportDoneでクラス変数をnullに設定。
 * 
 * importDataのJComponentと、クラス変数が同じ場合、ツリーパスの判定を行う。
 */
public class JTreeClipBoardTest2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		JFrame f = new JFrame();
		f .setBounds(400, 200, 550, 400);
		JTree t = new MyTree3();
		JTree t2 = new MyTree3();
//		t .setDragEnabled(false);
//		t2 .setDragEnabled(false);
		MouseListener [] mouse=t.getMouseListeners();
		for(MouseListener m:mouse){
			System.out.println(m.getClass());
		}
		
		t.addMouseMotionListener(new MouseMotionListener(){

			@Override
			public void mouseDragged(MouseEvent e) {
				// TODO Auto-generated method stub
				System.out.println("禁止拖动");
				return;
			}

			@Override
			public void mouseMoved(MouseEvent e) {
				// TODO Auto-generated method stub
				
			}});
		
		MouseMotionListener [] MouseMotion=t.getMouseMotionListeners();
		for(MouseMotionListener m:MouseMotion){
			System.out.println(m.getClass());
		}
		
		
		f.add(t, BorderLayout.CENTER);
		f.add(t2, BorderLayout.EAST);
		f.setSize(200, 200);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setVisible(true);
	}
}

/**
 * JTreeを継承して、初期化メソッドと、コピペ、DnD用のセッタゲッタを追加したクラス。
 */
class MyTree3 extends JTree {
	public MyTree3() {
		super();
		init();
	}

	private void init() {
		// ドラッグを許可します。
		setDragEnabled(true);
		// トランスファハンドラを設定します。
		setTransferHandler(new TreePathTransferHandler("selectionPathsDnd"));
		// ※selectionPathsDndは、内部でsetほにゃらら、getほにゃららのメソッド検索に使われます。
	}

	/**
	 * TransferHandlerのプロパティに対応するため、DnD、コピペ用のセッタゲッタを用意します。
	 * 
	 * @return
	 */
	public TreePath[] getSelectionPathsDnd() {
		return getSelectionPaths();
	}

	/**
	 * オブジェクトを受けとるインポートメソッド。
	 * 
	 * ここからクラスを判定して個別のメソッドを実行します。
	 * 
	 * 名前はDndついてますが、コピペでも同じメソッドが使われます。
	 * 
	 * @param paths
	 * @return
	 */
	public boolean setSelectionPathsDnd(Object obj) {
		System.out.println("setSelectionPathsDnd");
		if (obj == null) {
			return false;
		}
		if (obj instanceof TreePath[]) {
			TreePath[] paths = (TreePath[]) obj;
			return setSelectionPathsDnd(paths);
		}
		if (obj instanceof List) {
			List files = (List) obj;
			return setSelectionPathsDnd(files);
		}
		if (obj instanceof String) {
			String files = obj.toString();
			return setSelectionPathsDnd(files);
		}
		return false;
	}

	/**
	 * ファイルリスト用インポートメソッド
	 */
	public boolean setSelectionPathsDnd(List files) {
		if (files == null) {
			return false;
		}
		List filess = (List) files;
		javax.swing.JTree.DropLocation loc = getDropLocation();
		TreePath putPath = null;
		if (loc != null) {
			// ドロップしたパスを取得します。
			putPath = loc.getPath();
		} else {
			// ドロップロケーションがnullだったら、貼り付けと判断して選択済みのパスを取得します。
			putPath = getSelectionPath();
		}
		DefaultMutableTreeNode putNode = (DefaultMutableTreeNode) putPath
				.getLastPathComponent();
		for (Object file : filess) {
			DefaultMutableTreeNode node = new DefaultMutableTreeNode(
					((File) file).getName());
			putNode.add(node);
			((DefaultTreeModel) getModel()).reload(putNode);
		}
		return true;
	}

	/**
	 * ツリーパス用インポートメソッド
	 */
	public boolean setSelectionPathsDnd(TreePath[] paths) {
		if (paths == null) {
			return false;
		}
		javax.swing.JTree.DropLocation loc = getDropLocation();
		TreePath putPath = null;
		if (loc != null) {
			// ドロップしたパスを取得します。
			putPath = loc.getPath();
		} else {
			// ドロップロケーションがnullだったら、貼り付けと判断して選択済みのパスを取得します。
			putPath = getSelectionPath();
		}
		DefaultMutableTreeNode putNode = (DefaultMutableTreeNode) putPath
				.getLastPathComponent();
		for (TreePath path : paths) {
			DefaultMutableTreeNode node = (DefaultMutableTreeNode) path
					.getLastPathComponent();
			putNode.add(node);
			((DefaultTreeModel) getModel()).reload(putNode);
		}
		return true;
	}

	/**
	 * 文字列用インポートメソッド
	 */
	public boolean setSelectionPathsDnd(String str) {
		if (str == null) {
			return false;
		}
		javax.swing.JTree.DropLocation loc = getDropLocation();
		TreePath putPath = null;
		if (loc != null) {
			// ドロップしたパスを取得します。
			putPath = loc.getPath();
		} else {
			// ドロップロケーションがnullだったら、貼り付けと判断して選択済みのパスを取得します。
			putPath = getSelectionPath();
		}
		DefaultMutableTreeNode putNode = (DefaultMutableTreeNode) putPath
				.getLastPathComponent();
		String[] strAry = str.split("\n");
		for (String path : strAry) {
			DefaultMutableTreeNode node = new DefaultMutableTreeNode(path);
			putNode.add(node);
			((DefaultTreeModel) getModel()).reload(putNode);
		}
		return true;
	}
}

/**
 * ツリーパストランスファハンドラ
 */
class TreePathTransferHandler extends TransferHandler {
	JComponent comp;

	/**
	 * コンストラクタ プロパティ設定なし
	 */
	public TreePathTransferHandler() {
		super();
	}

	/**
	 * コンストラクタ プロパティ設定あり
	 */
	public TreePathTransferHandler(String property) {
		super(property);
	}

	/**
	 * 転送データ作成処理。
	 */
	@Override
	protected Transferable createTransferable(JComponent c) {
		if (c instanceof MyTree3) {
			MyTree3 tree = (MyTree3) c;
			Transferable tf = new TreePathTransferable(tree);
			return tf;
		}
		return null;
	}

	@Override
	public void exportAsDrag(JComponent comp, InputEvent e, int action) {
		this.comp = comp;
		super.exportAsDrag(comp, e, action);
	}

	/**
	 * アクションを返します。
	 */
	@Override
	public int getSourceActions(JComponent c) {
		// スーパークラスの戻り値がCOPYの場合、COPY_OR_MOVEに変換します。
		// ※スーパークラスがCOPYかNONEしか返さないため。
		int action = super.getSourceActions(c);
		if (action == COPY) {
			action = COPY_OR_MOVE;
		}
		return action;
	}

	@Override
	public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
		// 受け入れ可能かどうかを返します。
		// ツリーパスフレーバとファイルフレーバ以外のフレーバを受け入れないようにします。
		Transferable f = createTransferable(comp);
		boolean flg = false;
		for (DataFlavor df : transferFlavors) {
			if (f.isDataFlavorSupported(df)) {
				flg = true;
				break;
			}
		}
		return flg;
	}

	/**
	 * 出力側のドロップ完了処理。 actionがMOVEの場合、ツリーを削除します。
	 * ※importDataがfalseを返した場合、actionはNONEになります。
	 */
	@Override
	protected void exportDone(JComponent source, Transferable data, int action) {
		// 出力完了時にcompとnullに設定します。
		comp = null;
		if (!(source instanceof JTree)) {
			return;
		}
		MyTree3 tree = (MyTree3) source;
		if (action == MOVE) {
			try {
				for (DataFlavor df : data.getTransferDataFlavors()) {
					Object obj = data.getTransferData(df);
					// エクスポートの場合、処理するのはTreePathの場合だけでよい。
					if (obj instanceof TreePath[]) {
						TreePath[] patha = (TreePath[]) obj;
						TreePath path = patha[0];
						Object oo = path.getLastPathComponent();
						DefaultMutableTreeNode node = (DefaultMutableTreeNode) oo;
						node.removeFromParent();
						((DefaultTreeModel) tree.getModel()).reload(node);
					}
					if (obj instanceof List) {
						super.exportDone(source, data, action);
					}
				}
			} catch (UnsupportedFlavorException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			} catch (IOException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		} else {
			super.exportDone(source, data, action);
		}
	}

	/**
	 * 入力側のドロップ処理。 ドロップされたノードに、ドラッグされたノードを追加します。
	 */
	@Override
	public boolean importData(JComponent comp, Transferable t) {
		if (!(comp instanceof MyTree3)) {
			return false;
		}
		if (!canImport(comp, t.getTransferDataFlavors())) {
			return false;
		}
		if (!canImport2(comp, t)) {
			return false;
		}
		MyTree3 tree = (MyTree3) comp;
		DataFlavor[] dfa = t.getTransferDataFlavors();
		for (DataFlavor df : dfa) {
			try {
				if (tree.setSelectionPathsDnd(t.getTransferData(df))) {
					return true;
				}
			} catch (UnsupportedFlavorException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			} catch (IOException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}

		}
		return false;
	}

	private boolean canImport2(JComponent comp, Transferable t) {
		boolean flg = true;
		if (comp == this.comp) {
			try {
				// 同じJTreeにドロップする場合。
				MyTree3 tree = (MyTree3) comp;
				if (tree.getDropLocation() != null) {
					TreePath dropPath = tree.getDropLocation().getPath();
					TreePath[] paths = (TreePath[]) t
							.getTransferData(TreePathDataFravor.treePathFlavor);
					for (TreePath dragPath : paths) {
						if (dragPath.getPathCount() > dropPath.getPathCount()) {
							continue;
						}
						if (dropPath.equals(dragPath)) {
							flg = false;
							break;
						}
						Object[] dragPathObject = dragPath.getPath();
						Object[] dropPathObject = dropPath.getPath();
						boolean sameFlg = true;
						for (int i = 0; i < dragPathObject.length; i++) {
							if (!dragPathObject[i].toString().equals(
									dropPathObject[i].toString())) {
								sameFlg = false;
								break;
							}
						}
						if (sameFlg == true) {
							flg = false;
							break;
						}
					}
				} else {
					flg = false;
				}
			} catch (UnsupportedFlavorException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			} catch (IOException e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
		System.out.println(flg);
		return flg;
	}
}

/**
 * ツリーパスデータフレーバクラス。 作ったはいいけど、ツリーパスフレーバーの定数を保管しているだけである。
 */
class TreePathDataFravor extends DataFlavor {
	public static final DataFlavor treePathFlavor = new DataFlavor(
			TreePath[].class, javaJVMLocalObjectMimeType);

}

/**
 * ツリーパス用トランスファエイブル ツリーパスの配列と、データフレーバーの配列を持つ。
 */
class TreePathTransferable implements Transferable {
	TreePath[] paths;
	// ツリーパスフレーバとファイルリストフレーバ
	static final DataFlavor[] dflv = { TreePathDataFravor.treePathFlavor, // ツリーパスフレーバ
			TreePathDataFravor.javaFileListFlavor, // ファイルリストフレーバ
			TreePathDataFravor.stringFlavor // 文字列フレーバ
	};

	public TreePathTransferable(TreePath[] paths) {
		this.paths = paths;
	}

	public TreePathTransferable(MyTree3 tree) {
		this(tree.getSelectionPathsDnd());
	}

	/**
	 * オブジェクト取得メソッド。
	 * 
	 * フレーバに対応したオブジェクトを返します。
	 */
	@Override
	public Object getTransferData(DataFlavor flavor) {
		if (TreePathDataFravor.treePathFlavor.equals(flavor)) {
			// ツリーパスフレーバの場合
			return paths;
		} else if (TreePathDataFravor.javaFileListFlavor.equals(flavor)) {
			// ファイルリストフレーバの場合
			// ※偶然ファイルが存在しない限り、何も転送できない筈です。
			List files = new ArrayList();
			for (TreePath path : paths) {
				files.add(new File(path.getLastPathComponent().toString()));
			}
			return files;
		} else if (TreePathDataFravor.stringFlavor.equals(flavor)) {
			// 文字列フレーバの場合
			StringBuffer sb = new StringBuffer();
			for (TreePath path : paths) {
				sb.append(path.getLastPathComponent().toString() + "\n");
			}
			return sb.toString();
		}
		return null;
	}

	@Override
	public DataFlavor[] getTransferDataFlavors() {
		return dflv;
	}

	@Override
	public boolean isDataFlavorSupported(DataFlavor flavor) {
		DataFlavor[] flv = getTransferDataFlavors();
		for (int i = 0; i < flv.length; i++) {
			if (flv[i].equals(flavor)) {
				return true;
			}
		}
		return false;
	}
}